package top.huanyv.tools.utils;

import top.huanyv.tools.annotation.Column;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * JDBC工具类，需要在类路径下准备一个名字叫<code>jdbc.properties</code>的文件
 * <ol>
 *     <li>jdbc.driver 加载的jdbc驱动</li>
 *     <li>jdbc.url 数据库地址</li>
 *     <li>jdbc.username 用户名</li>
 *     <li>jdbc.password 密码</li>
 * </ol>
 */
public class JdbcUtil {

    /**
     * 是否开启下划线转驼峰
     */
    private static boolean mapUnderscoreToCamelCase = true;

    static {
        try {
            String driverName = PropertiesUtil.getProperty("jdbc.properties"
                    , "jdbc.driver", "com.mysql.jdbc.Driver");
            Class.forName(driverName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void setMapUnderscoreToCamelCase(boolean mapUnderscoreToCamelCase) {
        JdbcUtil.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase;
    }

    public static boolean getMapUnderscoreToCamelCase() {
        return JdbcUtil.mapUnderscoreToCamelCase;
    }

    /**
     * 从资料路径下读取文件名是 jdbc.properties 的文件
     * 获取 jdbc.url、jdbc.username、jdbc.password 属性
     *
     * @return Connection
     */
    public static Connection getConnection() {
        try {
            InputStream stream = ClassLoaderUtil.getInputStream("jdbc.properties");
            Properties properties = new Properties();
            properties.load(stream);
            String url = properties.getProperty("jdbc.url");
            String username = properties.getProperty("jdbc.username");
            String password = properties.getProperty("jdbc.password");
            return DriverManager.getConnection(url, username, password);
        } catch (SQLException | IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 传入数据库的  链接 用户名 密码
     *
     * @param url      链接
     * @param username 用户名
     * @param password 密码
     * @return Connection
     */
    public static Connection getConnection(String url, String username, String password) {
        try {
            return DriverManager.getConnection(url, username, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 关闭数据库连接对象、访问对象、查询结果集
     *
     * @param closeables 连接对象、访问对象、查询结果集
     */
    public static void close(AutoCloseable... closeables) {
        for (AutoCloseable closeable : closeables) {
            try {
                if (closeable != null) {
                    closeable.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static int executeUpdate(Connection connection, String sql, Object... args) throws SQLException {
        Assert.notNull(connection, "'connection' must not be null.");
        PreparedStatement ps = null;
        try {
            ps = connection.prepareStatement(sql);
            if (args != null && args.length != 0) {
                for (int i = 1; i <= args.length; i++) {
                    ps.setObject(i, args[i - 1]);
                }
            }
            return ps.executeUpdate();
        } finally {
            close(ps);
        }
    }

    /**
     * 执行insert语句，返回的是生成的主键
     *
     * @param connection 链接
     * @param sql        SQL语句
     * @param args       参数
     * @return 生成主键
     */
    public static String executeInsert(Connection connection, String sql, Object... args) throws SQLException {
        Assert.notNull(connection, "'connection' must not be null.");
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
            if (args != null && args.length > 0) {
                for (int i = 1; i <= args.length; i++) {
                    ps.setObject(i, args[i - 1]);
                }
            }
            ps.executeUpdate();
            rs = ps.getGeneratedKeys();
            if (rs.next()) {
                return rs.getString(1);
            }
        } finally {
            close(rs, ps);
        }
        return null;
    }

    public static ResultSet executeQuery(PreparedStatement ps, Object... args) throws SQLException {
        if (args != null && args.length != 0) {
            for (int i = 1; i <= args.length; i++) {
                ps.setString(i, args[i - 1].toString());
            }
        }
        return ps.executeQuery();
    }

    public static <T> T resultSetToBean(ResultSet rs, Class<T> type) throws SQLException {
        T t = ReflectUtil.newInstance(type);
        if (rs.next()) {
            if (t != null) {
                populateBean(rs, t);
            }
        }
        return t;
    }

    public static <T> List<T> resultSetToList(ResultSet rs, Class<T> type) throws SQLException {
        List<T> result = new ArrayList<>();
        while (rs.next()) {
            T t = ReflectUtil.newInstance(type);
            if (t != null) {
                populateBean(rs, t);
                result.add(t);
            }
        }
        return result;
    }

    public static Map<String, Object> resultSetToMap(ResultSet rs) throws SQLException {
        Map<String, Object> result = new HashMap<>();
        ResultSetMetaData metaData = rs.getMetaData();
        if (rs.next()) {
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnName(i);
                Object val = rs.getObject(columnName);
                result.put(columnName, val);
            }
        }
        return result;
    }

    public static List<Map<String, Object>> resultSetToMapList(ResultSet rs) throws SQLException {
        List<Map<String, Object>> result = new ArrayList<>();
        ResultSetMetaData metaData = rs.getMetaData();
        while (rs.next()) {
            Map<String, Object> map = new HashMap<>();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnName(i);
                Object val = rs.getObject(columnName);
                map.put(columnName, val);
            }
            result.add(map);
        }
        return result;
    }


    public static void populateBean(ResultSet rs, Object obj) throws SQLException {
        for (Field field : ReflectUtil.getAllDeclaredFields(obj.getClass())) {
            String columnName = field.getName();
            // 是否开启下划线转驼峰
            if (mapUnderscoreToCamelCase) {
                columnName = StringUtil.camelCaseToUnderscore(columnName);
            }
            // 是否用了注解标识
            Column column = field.getAnnotation(Column.class);
            columnName = column != null ? column.value() : columnName;
            Object val = rs.getObject(columnName);
            if (val != null) {
                ReflectUtil.setField(field, obj, val);
            }
        }
    }


}
